#!/usr/bin/python3 
# -*- coding: utf-8 -*-
############################################################
# *****************[ STK message constants ]***************************
MESSAGE_START                =       0x1B        #= ESC = 27 decimal
TOKEN                        =       0x0E
# ****************[ STK general command constants ]**************************
CMD_SIGN_ON                  =       0x01
CMD_SET_PARAMETER            =       0x02
CMD_GET_PARAMETER            =       0x03
CMD_SET_DEVICE_PARAMETERS    =       0x04
CMD_OSCCAL                   =       0x05
CMD_LOAD_ADDRESS             =       0x06
CMD_FIRMWARE_UPGRADE         =       0x07
# *****************[ STK ISP command constants ]******************************
CMD_ENTER_PROGMODE_ISP       =       0x10
CMD_LEAVE_PROGMODE_ISP       =       0x11
CMD_CHIP_ERASE_ISP           =       0x12
CMD_PROGRAM_FLASH_ISP        =       0x13
CMD_READ_FLASH_ISP           =       0x14
CMD_PROGRAM_EEPROM_ISP       =       0x15
CMD_READ_EEPROM_ISP          =       0x16
CMD_PROGRAM_FUSE_ISP         =       0x17
CMD_READ_FUSE_ISP            =       0x18
CMD_PROGRAM_LOCK_ISP         =       0x19
CMD_READ_LOCK_ISP            =       0x1A
CMD_READ_SIGNATURE_ISP       =       0x1B
CMD_READ_OSCCAL_ISP          =       0x1C
CMD_SPI_MULTI                =       0x1D
# *****************[ STK PP command constants ]*******************************
CMD_ENTER_PROGMODE_PP        =       0x20
CMD_LEAVE_PROGMODE_PP        =       0x21
CMD_CHIP_ERASE_PP            =       0x22
CMD_PROGRAM_FLASH_PP         =       0x23
CMD_READ_FLASH_PP            =       0x24
CMD_PROGRAM_EEPROM_PP        =       0x25
CMD_READ_EEPROM_PP           =       0x26
CMD_PROGRAM_FUSE_PP          =       0x27
CMD_READ_FUSE_PP             =       0x28
CMD_PROGRAM_LOCK_PP          =       0x29
CMD_READ_LOCK_PP             =       0x2A
CMD_READ_SIGNATURE_PP        =       0x2B
CMD_READ_OSCCAL_PP           =       0x2C    
CMD_SET_CONTROL_STACK        =       0x2D
# *****************[ STK HVSP command constants ]*****************************
CMD_ENTER_PROGMODE_HVSP      =       0x30
CMD_LEAVE_PROGMODE_HVSP      =       0x31
CMD_CHIP_ERASE_HVSP          =       0x32
CMD_PROGRAM_FLASH_HVSP       =       0x33
CMD_READ_FLASH_HVSP          =       0x34
CMD_PROGRAM_EEPROM_HVSP      =       0x35
CMD_READ_EEPROM_HVSP         =       0x36
CMD_PROGRAM_FUSE_HVSP        =       0x37
CMD_READ_FUSE_HVSP           =       0x38
CMD_PROGRAM_LOCK_HVSP        =       0x39
CMD_READ_LOCK_HVSP           =       0x3A
CMD_READ_SIGNATURE_HVSP      =       0x3B
CMD_READ_OSCCAL_HVSP         =       0x3C
# *****************[ STK status constants ]***************************
STATUS_CMD_OK                 =      0x00 # Success
STATUS_CMD_TOUT               =      0x80 # Warnings
STATUS_RDY_BSY_TOUT           =      0x81 # Warnings
STATUS_SET_PARAM_MISSING      =      0x82 # Warnings
STATUS_CMD_FAILED             =      0xC0 # Errors
STATUS_CKSUM_ERROR            =      0xC1 # Errors
STATUS_CMD_UNKNOWN            =      0xC9 # Errors
# *****************[ STK parameter constants ]***************************
PARAM_BUILD_NUMBER_LOW        =      0x80
PARAM_BUILD_NUMBER_HIGH       =      0x81
PARAM_HW_VER                  =      0x90
PARAM_SW_MAJOR                =      0x91
PARAM_SW_MINOR                =      0x92
PARAM_VTARGET                 =      0x94
PARAM_VADJUST                 =      0x95
PARAM_OSC_PSCALE              =      0x96
PARAM_OSC_CMATCH              =      0x97
PARAM_SCK_DURATION            =      0x98
PARAM_TOPCARD_DETECT          =      0x9A
PARAM_STATUS                  =      0x9C
PARAM_DATA                    =      0x9D
PARAM_RESET_POLARITY          =      0x9E
PARAM_CONTROLLER_INIT         =      0x9F
# *****************[ STK answer constants ]***************************
ANSWER_CKSUM_ERROR            =      0xB0

#HW and SW version, used for CMD_GET_PARAMETER
CONFIG_PARAM_BUILD_NUMBER_LOW  = 0    # ack for PARAM_BUILD_NUMBER_LOW
CONFIG_PARAM_BUILD_NUMBER_HIGH = 0    # ack for PARAM_BUILD_NUMBER_HIGH
CONFIG_PARAM_HW_VER            = 0x0F # ack for PARAM_HW_VER
CONFIG_PARAM_SW_MAJOR          = 2    # ack for PARAM_SW_MAJOR
CONFIG_PARAM_SW_MINOR          = 0x0A # ack for PARAM_SW_MINOR
CONFIG_PARAM_DEFAULT           = 0    # ack for other PARAM_XXX_XXX
############################################################
import platform 
import serial
global SerialHdl
def ser_open():
	global SerialHdl
	if platform.system()=='Linux':
		sdev = '/dev/pts/3'    # use virtual com created by [virtualcom.py]
		#sdev = '/dev/ttyACM0'  # arduino com port on linux
		#sdev = '/dev/ttyUSB0'   # USB2Serial device on linux
	else:
		sdev   = 'COM23'      # windows
	
	bps    = 115200       # 9600,19200,38400,57600,115200
	timout = 1000            # timeout (second) / None : waiting forever
	try:
		SerialHdl = serial.Serial(sdev, bps, bytesize=8, parity='N', stopbits=1, timeout=timout)
	except Exception as e:
		print('error, can not open [%s]: ', sdev, e)
		SerialHdl = None

	return SerialHdl

def ser():
	global SerialHdl
	return SerialHdl

def ser_close():
	global SerialHdl
	ser().close()
	SerialHdl = None

############################################################
SM_START    =  0
SM_SEQ      =  1
SM_SIZE1    =  2
SM_SIZE0    =  3
SM_TOKEN    =  4
SM_DATA     =  5
SM_CHECKSUM =  6
global Stk500v2State
Stk500v2State = {
	'state' : SM_START,       #current state
	'buf'   : [0]*300, #bytearray(100), #stk500v2 frame buffer
	'bsz'   : 300,            #buffer size: 300 bytes
	'bidx'  : 0,              #buffer index
	'datcnt': 0,
	'frmlen': 0
}
############################################################
global McuFlash_16kX16bit
#32kBytes = 16K*16 bit
McuFlash_16kX16bit = [0xFFFF]*(16*1024)
global FlashProAddr
FlashProAddr = 0 #range 0~16384 (0~16K)
def FlashErease():
	global McuFlash_16kX16bit
	for i in range(0,len(McuFlash_16kX16bit)):
		McuFlash_16kX16bit[i] = 0xFFFF
#
def FlashProAddr_set(addr):
	global FlashProAddr
	FlashProAddr = addr

def FlashProWriteWord(word):
	global McuFlash_16kX16bit
	global FlashProAddr
	McuFlash_16kX16bit[FlashProAddr] = word
	FlashProAddr += 1
def FlashProReadWord():
	global McuFlash_16kX16bit
	global FlashProAddr
	word = McuFlash_16kX16bit[FlashProAddr]
	FlashProAddr += 1
	return word

global McuEEProm_1024Byte
McuEEProm_1024Byte = [0xFF]*1024
def EEPromWriteByte(byte_val):
	global McuEEProm_1024Byte
	global FlashProAddr
	McuEEProm_1024Byte[FlashProAddr] = byte_val
	FlashProAddr += 1
def EEPromReadByte():
	global McuEEProm_1024Byte
	global FlashProAddr
	ret = McuEEProm_1024Byte[FlashProAddr]
	FlashProAddr += 1
	return ret
############################################################
#
def LOG(str):
	str#print(str)
#
def sm():
	global Stk500v2State
	return Stk500v2State
#store a byte into buffer
def buf_append_byte(byte_val):
	if sm()['bidx']<sm()['bsz']:
		sm()['buf'][sm()['bidx']] = byte_val
		sm()['bidx'] += 1
		return True
	else:
		return False
#get byte value in buffer
def buf_get_byte(idx):
	return sm()['buf'][idx]
#
def buf_set_byte(byte_val, idx):
	sm()['buf'][idx] = byte_val
#get/set state
def sm_state(state=None):
	if state!=None:
		sm()['state'] = state
	return sm()['state']
#set data count
def sm_set_datcnt(cnt):
	LOG('data sum: %02X'%cnt)
	sm()['datcnt'] = cnt
#get data count	
def sm_get_datcnt():
	return sm()['datcnt']
#
def sm_set_frmlen(len):
	sm()['frmlen'] = len
#
def sm_get_frmlen():
	return sm()['frmlen']
#clear state
def sm_reset():
	sm()['state']  = SM_START
	sm()['bidx']   = 0
	sm()['datcnt'] = 0
	sm()['frmlen'] = 0
#calculate 8bit XOR checksum
def cal_checksum( len ):
	cksum = 0x00
	for idx in range(len):
		cksum ^= buf_get_byte(idx)
	return cksum
#print frame
#from_side : 'PC' - PC
#            'STK' - simulator
def print_frame(from_side, cmd_name):
	buf = sm()['buf']
	sz  = sm()['frmlen']
	if from_side=='PC':
		print('===  %s  ==='%cmd_name)
		print('PC  : '+('%02X '*sz)%tuple(buf[:sz]))
	else:
		print('STK : '+('%02X '*sz)%tuple(buf[:sz]))
########################
#
def do_ACK_OK():
	#ACK : 1B XX 00 02 0E XX 00 NN    # OK
	#byte[0]-byte[2] no change
	buf_set_byte(0x02, 3) #SIZE0
	#byte[4]-byte[5] no change
	buf_set_byte(0x00, 6)
	return 8
#save ACK frame into buffer
def do_CMD_SIGN_ON():
	#ACK: 1B 01 00 0B 0E 01 00 08 53 54 4B 35 30 30 5F 32 02  #STK500_2
	#byte[0]-byte[2] no change
	buf_set_byte(0x0B, 3) #SIZE0
	#byte[4]-byte[5] no change
	buf_set_byte(0x00, 6) #OK
	buf_set_byte(0x08, 7)
	#############################
	#STK500_2
	buf_set_byte(0x53, 8)
	buf_set_byte(0x54, 9)
	buf_set_byte(0x4B, 10)
	buf_set_byte(0x35, 11)
	buf_set_byte(0x30, 12)
	buf_set_byte(0x30, 13)
	buf_set_byte(0x5F, 14)
	buf_set_byte(0x32, 15)

	#AVRISP_2 41 56 52 49 53 50 
	#buf_set_byte(0x41, 8)
	#buf_set_byte(0x56, 9)
	#buf_set_byte(0x52, 10)
	#buf_set_byte(0x49, 11)
	#buf_set_byte(0x53, 12)
	#buf_set_byte(0x50, 13)
	#buf_set_byte(0x5F, 14)
	#buf_set_byte(0x32, 15)
	#############################
	return 17 #frame length
def do_CMD_SET_PARAMETER():
	#do something
	return do_ACK_OK()
def do_CMD_GET_PARAMETER():
	#PC  : 1B 0A 00 02 0E 03 97 89 
	#STK : 1B 0A 00 03 0E 03 OK XX CC
	param = buf_get_byte(6)
	buf_set_byte(0x00, 6) #OK
	if param==PARAM_BUILD_NUMBER_LOW:
		param = CONFIG_PARAM_BUILD_NUMBER_LOW
	elif param==PARAM_BUILD_NUMBER_HIGH:
		param = CONFIG_PARAM_BUILD_NUMBER_HIGH
	elif param==PARAM_HW_VER:
		param = CONFIG_PARAM_HW_VER
	elif param==PARAM_SW_MAJOR:
		param = CONFIG_PARAM_SW_MAJOR
	elif param==PARAM_SW_MINOR:	
		param = CONFIG_PARAM_SW_MINOR
	else:
		param = CONFIG_PARAM_DEFAULT
	buf_set_byte(param, 7)
	return 9
def do_CMD_SET_DEVICE_PARAMETERS():
	print('empty function')
def do_CMD_OSCCAL():
	print('empty function')
def do_CMD_LOAD_ADDRESS():
	#PC  : 1B 0E 00 05 0E 06 00 00 00 00 18 
	#STK : 
	addr = buf_get_byte(8)*256+buf_get_byte(9)
	FlashProAddr_set(addr)
	return do_ACK_OK()
def do_CMD_FIRMWARE_UPGRADE():
	print('empty function')
def do_CMD_ENTER_PROGMODE_ISP():
	#do something
	return do_ACK_OK()
def do_CMD_LEAVE_PROGMODE_ISP():
	#PC  : 1B A7 00 03 0E 11 01 01 A0
	return do_ACK_OK()
def do_CMD_CHIP_ERASE_ISP():
	#PC  : 1B 10 00 07 0E 12 09 00 AC 91 55 00 71
	FlashErease()
	return do_ACK_OK()
def do_CMD_PROGRAM_FLASH_ISP():
	#do something
	#PC  : 1B 6F 00 8A 0E 13 00 80 C1 06 40 4C 20 FF FF DB0 DB1 DB2 ... DB127 19
	for i in range(0,(buf_get_byte(6)*256+buf_get_byte(7)),2):
		FlashProWriteWord(buf_get_byte(15+i)*256+buf_get_byte(16+i))
	return do_ACK_OK()

def do_CMD_READ_FLASH_ISP():
	#PC  : 1B 75 00 04 0E 14 01 00 20 51 
	#STK : 1B 75 01 03 0E 14 00 XX XX (n bytes) XX 00 CC
	read_len = buf_get_byte(6)*256+buf_get_byte(7)
	#byte[0]-byte[2] no change
	buf_set_byte(((read_len+3)&0xFF00)>>8, 2) #SIZE1
	buf_set_byte((read_len+3)&0xFF, 3) #SIZE0
	buf_set_byte(0x00, 6)
	#7~N
	for i in range(7,7+read_len, 2):
		word = FlashProReadWord()
		buf_set_byte((word>>8), i)
		buf_set_byte((word&0xFF), i+1)
	buf_set_byte(0x00, 7+read_len)
	return read_len + 3 + 6
def do_CMD_PROGRAM_EEPROM_ISP():
	#PC  : 1B 1F 00 0E 0E 15 00 04 C1 14 C1 C2 A0 FF FF 11 22 33 44 27
	#STK : ACK_OK
	wt_sz1 = buf_get_byte(6)
	wt_sz0 = buf_get_byte(7)
	if((wt_sz1==0) and (wt_sz0<=64)):
		for i in range(15, 15+wt_sz0):
			EEPromWriteByte(buf_get_byte(i))
		return do_ACK_OK()
	else:
		return 0
	print('empty function')
def do_CMD_READ_EEPROM_ISP():
	#PC  : 1B 13 00 04 0E 16 00 04 A0 B0
	#STK : 1B 13 00 XX 0E 16 00 XX XX XX XX 00 CC
	read_len = buf_get_byte(6)*256+buf_get_byte(7)
	#byte[0]-byte[1] no change
	buf_set_byte(((read_len+3)&0xFF00)>>8, 2) #SIZE1
	buf_set_byte((read_len+3)&0xFF, 3) #SIZE0
	buf_set_byte(0x00, 6)
	#7~N
	for i in range(7,7+read_len):
		buf_set_byte(EEPromReadByte(), i)
	buf_set_byte(0x00, 7+read_len)
	return read_len + 3 + 6
def do_CMD_PROGRAM_FUSE_ISP():
	print('empty function')
#The device has three Fuse bytes. 
#Read Fuse bits         : 0x50 0x00 0x00
#Read Fuse High bits    : 0x58 0x08 0x00 
#Read Extended Fuse bits: 0x50 0x08 0x00
def do_CMD_READ_FUSE_ISP():
	#PC  : 1B 07 00 06 0E 18 04 50 00 00 00 58
	#STK : 1B 07 00 04 0E 18 00 XX 00 CC
	#byte[0]-byte[2] no change
	buf_set_byte(0x04, 3) #SIZE0
	#byte[4]-byte[5] no change
	buf_set_byte(0x00, 6) #OK
	#
	fuse_type = buf_get_byte(7)+buf_get_byte(8)
	if fuse_type==(0x50+0x00):
		#Fuse bits
        # Table 31-7. Fuse Low Byte
        # Low Fuse Byte | Bit No. | Description         |   Default Value
        #-------------------------------------------------------------------------
        #    CKDIV8(4)  |    7    | Divide clock by 8   |        0 (programmed)
        #     CKOUT(3)  |    6    | Clock output        |        1 (unprogrammed)
        #        SUT1   |    5    | Select start-up time|        1 (unprogrammed)
        #      SUT0     |    4    | Select start-up time|        0 (programmed)(1)
        #      CKSEL3   |    3    | Select Clock source |        0 (programmed)(2)
        #      CKSEL2   |    2    | Select Clock source |        0 (programmed)(2)
        #      CKSEL1   |    1    | Select Clock source |        1 (unprogrammed)(2)
        #      CKSEL0   |    0    | Select Clock source |        0 (programmed)
        #-------------------------------------------------------------------------
		buf_set_byte(0x62, 7)
	elif fuse_type==(0x58+0x08):
		#Fuse High bits
		# Table 31-6. Fuse High Byte
		# High Fuse Byte | Bit No.| Description                                      |  Default Value
		#---------------------------------------------------------------------------------------------------
		# RSTDISBL(1)    |    7   | External Reset Disable                           |      1 (unprogrammed)
		# DWEN           |    6   | debugWIRE Enable                                 |      1 (unprogrammed)
		# SPIEN(2)       |    5   | Enable Serial Program and Data Downloading       |      0 (programmed, SPI programming enabled)
		# WDTON(3)       |    4   | Watchdog Timer Always On                         |      1 (unprogrammed)
		# EESAVE         |    3   | EEPROM memory is preserved through the Chip Erase|      1 (unprogrammed), EEPROM not reserved
		# BOOTSZ1        |    2   | Select Boot Size (see Boot Loader Parameters)    |      0 (programmed)(4)
		# BOOTSZ0        |    1   | Select Boot Size (see Boot Loader Parameters)    |      0 (programmed)(4)
		# BOOTRST        |    0   | Select Reset Vector                              |      1 (unprogrammed)
		#---------------------------------------------------------------------------------------------------
		buf_set_byte(0xD9, 7)
	else:
		#Extended Fuse bits
		buf_set_byte(0xFF, 7)
	#	
	buf_set_byte(0x00, 8) #OK
	return 10 #frame length

def do_CMD_PROGRAM_LOCK_ISP():
	print('empty function')
def do_CMD_READ_LOCK_ISP():
	print('empty function')
#######################################
def getSignature(sig_adr):
	if sig_adr==0:
		return 0x1E 
	elif sig_adr==1:
		return 0x95
	elif sig_adr==2:
		return 0x0F
#######################################
def do_CMD_READ_SIGNATURE_ISP():
	#PC : 1B 04 00 06 0E 1B 04 30 00 00 00 38
	#STK: 1B 04 00 04 0E 1B 00 XX 00 CC
	#byte[0]-byte[2] no change
	buf_set_byte(0x04, 3) #SIZE0
	#byte[4]-byte[5] no change
	buf_set_byte(0x00, 6) #OK
	#signature for ATmega328P is 1E 95 0F
	buf_set_byte(getSignature(buf_get_byte(9)), 7) # SIGNATURE byte value
	buf_set_byte(0x00, 8) #OK
	return 10 #frame length

def do_CMD_READ_OSCCAL_ISP():
	print('empty function')
def do_CMD_SPI_MULTI():
	#PC  : 1B 04 00 08 0E 1D 04 04 00 30 00 00 00 34 
	#STK : 1B 04 00 XX 0E 1D 00 XX XX XX XX 00 CC

	#PC  : 1B 07 00 08 0E 1D 04 04 00 A0 03 FC 00 58 
	#STK : 1B 04 00 XX 0E 1D 00 XX XX XX XX 00 CC
	send_len = buf_get_byte(6)
	recv_len = buf_get_byte(7)
	#byte[0]-byte[2] no change
	buf_set_byte(recv_len+3, 3) #SIZE0
	#byte[4]-byte[5] no change
	buf_set_byte(0x00, 6) #OK
	#
	if buf_get_byte(9)==0x30:
		buf_set_byte( getSignature(buf_get_byte(11)), 10)#sigbyte
		buf_set_byte(0x00, 11) #OK
	elif buf_get_byte(9)==0xA0:
		buf_set_byte(0xEE, 10)#
		buf_set_byte(0x00, 11) #OK

	return buf_get_byte(3)+6
########################
global Stk500v2CmdName
Stk500v2CmdName = {
	CMD_SIGN_ON                 : 'CMD_SIGN_ON',
	CMD_SET_PARAMETER           : 'CMD_SET_PARAMETER',
	CMD_GET_PARAMETER           : 'CMD_GET_PARAMETER',
	CMD_SET_DEVICE_PARAMETERS   : 'CMD_SET_DEVICE_PARAMETERS',
	CMD_OSCCAL                  : 'CMD_OSCCAL',
	CMD_LOAD_ADDRESS            : 'CMD_LOAD_ADDRESS',
	CMD_FIRMWARE_UPGRADE        : 'CMD_FIRMWARE_UPGRADE',
	CMD_ENTER_PROGMODE_ISP      : 'CMD_ENTER_PROGMODE_ISP',
	CMD_LEAVE_PROGMODE_ISP      : 'CMD_LEAVE_PROGMODE_ISP',
	CMD_CHIP_ERASE_ISP          : 'CMD_CHIP_ERASE_ISP',
	CMD_PROGRAM_FLASH_ISP       : 'CMD_PROGRAM_FLASH_ISP',
	CMD_READ_FLASH_ISP          : 'CMD_READ_FLASH_ISP',
	CMD_PROGRAM_EEPROM_ISP      : 'CMD_PROGRAM_EEPROM_ISP',
	CMD_READ_EEPROM_ISP         : 'CMD_READ_EEPROM_ISP',
	CMD_PROGRAM_FUSE_ISP        : 'CMD_PROGRAM_FUSE_ISP',
	CMD_READ_FUSE_ISP           : 'CMD_READ_FUSE_ISP',
	CMD_PROGRAM_LOCK_ISP        : 'CMD_PROGRAM_LOCK_ISP',
	CMD_READ_LOCK_ISP           : 'CMD_READ_LOCK_ISP',
	CMD_READ_SIGNATURE_ISP      : 'CMD_READ_SIGNATURE_ISP',
	CMD_READ_OSCCAL_ISP         : 'CMD_READ_OSCCAL_ISP',
	CMD_SPI_MULTI               : 'CMD_SPI_MULTI',
	CMD_ENTER_PROGMODE_PP       : 'CMD_ENTER_PROGMODE_PP',
	CMD_LEAVE_PROGMODE_PP       : 'CMD_LEAVE_PROGMODE_PP',
	CMD_CHIP_ERASE_PP           : 'CMD_CHIP_ERASE_PP',
	CMD_PROGRAM_FLASH_PP        : 'CMD_PROGRAM_FLASH_PP',
	CMD_READ_FLASH_PP           : 'CMD_READ_FLASH_PP',
	CMD_PROGRAM_EEPROM_PP       : 'CMD_PROGRAM_EEPROM_PP',
	CMD_READ_EEPROM_PP          : 'CMD_READ_EEPROM_PP',
	CMD_PROGRAM_FUSE_PP         : 'CMD_PROGRAM_FUSE_PP',
	CMD_READ_FUSE_PP            : 'CMD_READ_FUSE_PP',
	CMD_PROGRAM_LOCK_PP         : 'CMD_PROGRAM_LOCK_PP',
	CMD_READ_LOCK_PP            : 'CMD_READ_LOCK_PP',
	CMD_READ_SIGNATURE_PP       : 'CMD_READ_SIGNATURE_PP',
	CMD_READ_OSCCAL_PP          : 'CMD_READ_OSCCAL_PP',
	CMD_SET_CONTROL_STACK       : 'CMD_SET_CONTROL_STACK',
	CMD_ENTER_PROGMODE_HVSP     : 'CMD_ENTER_PROGMODE_HVSP',
	CMD_LEAVE_PROGMODE_HVSP     : 'CMD_LEAVE_PROGMODE_HVSP',
	CMD_CHIP_ERASE_HVSP         : 'CMD_CHIP_ERASE_HVSP',
	CMD_PROGRAM_FLASH_HVSP      : 'CMD_PROGRAM_FLASH_HVSP',
	CMD_READ_FLASH_HVSP         : 'CMD_READ_FLASH_HVSP',
	CMD_PROGRAM_EEPROM_HVSP     : 'CMD_PROGRAM_EEPROM_HVSP',
	CMD_READ_EEPROM_HVSP        : 'CMD_READ_EEPROM_HVSP',
	CMD_PROGRAM_FUSE_HVSP       : 'CMD_PROGRAM_FUSE_HVSP',
	CMD_READ_FUSE_HVSP          : 'CMD_READ_FUSE_HVSP',
	CMD_PROGRAM_LOCK_HVSP       : 'CMD_PROGRAM_LOCK_HVSP',
	CMD_READ_LOCK_HVSP          : 'CMD_READ_LOCK_HVSP',
	CMD_READ_SIGNATURE_HVSP     : 'CMD_READ_SIGNATURE_HVSP',
	CMD_READ_OSCCAL_HVSP        : 'CMD_READ_OSCCAL_HVSP'
}
global Stk500v2CmdAction
Stk500v2CmdAction = {
	CMD_SIGN_ON                 : do_CMD_SIGN_ON,
	CMD_SET_PARAMETER           : do_CMD_SET_PARAMETER,
	CMD_GET_PARAMETER           : do_CMD_GET_PARAMETER,
	CMD_SET_DEVICE_PARAMETERS   : do_CMD_SET_DEVICE_PARAMETERS,
	CMD_OSCCAL                  : do_CMD_OSCCAL,
	CMD_LOAD_ADDRESS            : do_CMD_LOAD_ADDRESS,
	CMD_FIRMWARE_UPGRADE        : do_CMD_FIRMWARE_UPGRADE,
	CMD_ENTER_PROGMODE_ISP      : do_CMD_ENTER_PROGMODE_ISP,
	CMD_LEAVE_PROGMODE_ISP      : do_CMD_LEAVE_PROGMODE_ISP,
	CMD_CHIP_ERASE_ISP          : do_CMD_CHIP_ERASE_ISP,
	CMD_PROGRAM_FLASH_ISP       : do_CMD_PROGRAM_FLASH_ISP,
	CMD_READ_FLASH_ISP          : do_CMD_READ_FLASH_ISP,
	CMD_PROGRAM_EEPROM_ISP      : do_CMD_PROGRAM_EEPROM_ISP,
	CMD_READ_EEPROM_ISP         : do_CMD_READ_EEPROM_ISP,
	CMD_PROGRAM_FUSE_ISP        : do_CMD_PROGRAM_FUSE_ISP,
	CMD_READ_FUSE_ISP           : do_CMD_READ_FUSE_ISP,
	CMD_PROGRAM_LOCK_ISP        : do_CMD_PROGRAM_LOCK_ISP,
	CMD_READ_LOCK_ISP           : do_CMD_READ_LOCK_ISP,
	CMD_READ_SIGNATURE_ISP      : do_CMD_READ_SIGNATURE_ISP,
	CMD_READ_OSCCAL_ISP         : do_CMD_READ_OSCCAL_ISP,
	CMD_SPI_MULTI               : do_CMD_SPI_MULTI
}
#
def sm_cmd_unknown():
	print('error unknown cmd!!')
	return 0
#execute cmd
def sm_do_cmd():
	global Stk500v2CmdName
	global Stk500v2CmdAction
	cmd   = buf_get_byte(5)
	cname = Stk500v2CmdName.get(cmd, 'CMD_UNKNOWN')
	print_frame('PC', cname) 
	action = Stk500v2CmdAction.get(cmd, sm_cmd_unknown)
	ack_frame_len = action()
	if ack_frame_len!=None and ack_frame_len>0:
		buf_set_byte(cal_checksum(ack_frame_len-1), ack_frame_len-1) #add checksum of the ACK to buffer
		sm_set_frmlen(ack_frame_len)
		if cname!='CMD_UNKNOWN':
			print_frame('STK', cname) 
		else:
			print('STK : xxxxxxxxx')
		
		ser().write(bytes((sm()['buf'])[:sm_get_frmlen()]))
#simulator state machine
def sm_run():
	loop = True
	while loop:
		ch = ser().read() #read one byte
		if ch==b'':
			print('read timeout')
			loop = False
			break
		#state swich-case
		ch    = ord(ch)
		state = sm_state()
		if state==SM_START:
			if ch==MESSAGE_START:
				#frame start
				LOG('START: %02X'%ch)
				buf_append_byte(ch)
				sm_state(SM_SEQ)
			else:
				LOG('error, not MESSAGE_START')
		elif state==SM_SEQ:
			LOG('SEQ  : %02X'%ch)
			buf_append_byte(ch)
			sm_state(SM_SIZE1)
		elif state==SM_SIZE1:
			LOG('SIZE1: %02X'%ch)
			buf_append_byte(ch)
			sm_state(SM_SIZE0)
		elif state==SM_SIZE0:
			LOG('SIZE0: %02X'%ch)
			buf_append_byte(ch)
			data_sum = buf_get_byte(2)*256+ch
			sm_set_datcnt(data_sum)   #cal data length
			sm_set_frmlen(data_sum+6) #whole frame length
			if data_sum==0:
				LOG('error, data sum is 0')
				sm_reset() #start to receive a new frame
			else:
				sm_state(SM_TOKEN)
		elif state==SM_TOKEN:
			if ch==TOKEN:
				LOG('TOKEN: %02X'%ch)
				buf_append_byte(ch)
				sm_state(SM_DATA)
			else:
				LOG('error, not TOKEN')
				sm_reset() #start to receive a new frame
		elif state==SM_DATA:
			buf_append_byte(ch)
			datsum = sm_get_datcnt()-1
			sm_set_datcnt(datsum)
			if datsum==0:
				LOG('end of data')
				sm_state(SM_CHECKSUM)
		elif state==SM_CHECKSUM:
			LOG('CHSUM: %02X'%ch)
			buf_append_byte(ch)
			if cal_checksum(sm_get_frmlen()-1)==ch:
				sm_do_cmd()
			else:
				LOG('error,checksum dismatch')
			sm_reset()
def main():
	'''
	avrdude -c stk500v2 -P COM11 -p m328p -e -U flash:w:bin/arduino.hex:i
	avrdude -c stk500v2 -P /dev/pts/2 -p m328p -e -U flash:w:bin/arduino.hex:i
	===CMD_SIGN_ON===
	PC  : 1B 01 00 01 0E 01 14 
	STK : 1B 01 00 0B 0E 01 00 08 53 54 4B 35 30 30 5F 32 02  #STK500_2
	===CMD_SET_PARAMETER===
	PC  : 1B 02 00 03 0E 02 9E 01 89 # PARAM_RESET_POLARITY
	STK : 1B 02 00 02 0E 02 00 17    # OK
	===CMD_ENTER_PROGMODE_ISP===
	PC  : 1B 03 00 0C 0E 10 C8 64 19 20 00 53 03 AC 53 00 00 30
	STK : 1B 03 00 02 0E 10 00 04    # OK
	===CMD_SPI_MULTI===
	PC  : 1B 04 00 08 0E 1D 04 04 00 30 00 00 00 34 
	STK : 1B 04 00 XX 0E 1D xx xx ....
	
	'''
	print('========================================================')
	print('================= STK500_V2 simulator ==================')
	print('========================================================')
	print('########################################################')
	print('# connection:                                          #')
	print('#                                                      #')
	print('#                   |-TX ----> RX-|                    #')
	print('#  avrdude-->[COMx]-+             +-[COMy]<--simulator #')
	print('#                   |-RX <---- TX-|                    #')
	print('#                                                      #')
	print('########################################################')
	if ser_open()!=None:
		sm_run()
		ser_close()
	print('######################## STOP ##########################')
	return

main()